import {clone, equals, isNil, mergeDeepWith, type} from 'ramda'
/**
 * 默认携带当前值的key与parent，便于delete操作等
 */
interface DeepScope {
    key: string,
    parent: any,
    [prop: string]: any;
}

/**
 * 获得翻译转换后的配置,当前版本仅翻译匹配成功的字符串
 * @param data
 * @param scope
 * @param convertFunc
 * @param isTerminator
 */
export function deepConvert(data, scope: DeepScope, convertFunc: Function, isTerminator: Function) {
    // todo 是否应区分scope中的临时可变数据(key,parent)，与常驻不变数据(dataScope等其他外部传入的数据)
    if(isTerminator(data, scope) === 'deep') {
        return deepConvert(convertFunc(data, scope), scope, convertFunc, isTerminator)
    }
    if(isTerminator(data, scope)) return convertFunc(data, scope) // 最优先判断，是否为终止标记
    if(['[object Object]','[object Array]'].includes(Object.prototype.toString.call(data))) {
        // 初始化容器类型
        let container = null
        if(Object.prototype.toString.call(data) === '[object Array]') {
            container = []
        }
        if(Object.prototype.toString.call(data) === '[object Object]') {
            container = {}
        }
        for(let key in data) {
            scope.key = key
            scope.parent = container
            if(Object.prototype.toString.call(container) === '[object Object]') {
                container[key] = deepConvert(data[key], scope, convertFunc, isTerminator)
            }else {
                let tempResult = deepConvert(data[key], scope, convertFunc, isTerminator)
                if(tempResult !== undefined) container.push(deepConvert(data[key], scope, convertFunc, isTerminator))
            }
        }
        return container
    }
    return data
}

/**
 * 代替直接赋值方式，合并两个对象，尽可能的不改变指针，键名以obj2为主，obj1中存在，obj2中不存在的键名，将会删除obj1中的内容
 * @param obj1
 * @param obj2
 */
export function deepMerge(obj1, obj2) {
    if(Object.prototype.toString.call(obj1) === '[object Array]') {
        obj1.length = 0
        obj1.splice(0,0,...obj2)
        return
    }
    for(let key in obj1) {
        if(!obj2.hasOwnProperty(key)) {
            delete obj1[key]
        }
    }
    for(let key in obj1) {
        if(!equals(obj1[key], obj2[key])) { // todo 使用equals将增加时空复杂度，待优化
            if(Object.prototype.toString.call(obj1[key]) === '[object Object]') {
                deepMerge(obj1[key], obj2[key])
            }else if(Object.prototype.toString.call(obj1[key]) === '[object Array]') {
                obj1[key].length = 0
                obj1[key].splice(0,0,...obj2[key])
            }else {
                obj1[key] = obj2[key]
            }
        }
    }
    for(let key in obj2) {
        if(!obj1.hasOwnProperty(key)) obj1[key] = obj2[key]
    }
}

/**
 * 代替object.assign，合并两个对象，尽可能的不改变指针
 * @param obj1
 * @param obj2
 * @param [matchFunc]
 */
export function deepAssign(obj1 = {}, obj2 = {}, matchFunc = null, notUseMatch = null) {
    for(let key in obj2) {
        // 属性判断类型
        if(obj1.hasOwnProperty(key) && type(obj2[key]) === 'Object' && type(obj1[key]) === 'Object') {
            deepAssign(obj1[key], obj2[key], matchFunc, notUseMatch)
        }else if(obj1.hasOwnProperty(key) && type(obj2[key]) === 'Array' && type(obj1[key]) === 'Array') {
            deepAssignArr(obj1[key], obj2[key], matchFunc, notUseMatch)
        }else {
            obj1[key] = obj2[key]
        }
    }
    return obj1
}
export function shallowAssign(obj1 = {}, obj2 = {}, matchFunc = null) {
    for(let key in obj2) {
        // 属性判断类型
        if(obj1.hasOwnProperty(key) && type(obj2[key]) === 'Object' && type(obj1[key]) === 'Object') {
            deepAssign(obj1[key], obj2[key], matchFunc)
        }else {
            obj1[key] = obj2[key]
        }
    }
    return obj1
}

/**
 * 合并数组
 * @param arr1
 * @param arr2
 * @param [matchFunc] 例：(a,b) => a.id === b.id
 * @param [notUseMatch] 例：(arr1,arr2) => !a?.[0]?.id || b?.[0]?.id 为true时，不适用标识配对情况，仍以顺序覆盖
 */
export function deepAssignArr(arr1, arr2, matchFunc = null, notUseMatch = null) {
    if(arr2.filter(item => typeof item === 'object')?.length === 0) {
        arr1.splice(0, arr1.length)
    }
    for (let index in arr2) {
        if(type(arr2[index]) === 'Object') {
            // 没有自定义匹配规则时使用，顺位赋值
            if((notUseMatch instanceof Function && notUseMatch(arr1, arr2)) || !(matchFunc instanceof Function)) {
                if(!isNil(arr1[index])) {
                    deepAssign(arr1[index], arr2[index], matchFunc, notUseMatch);
                }else {
                    arr1.push(arr2[index])
                }
            }
            // 匹配规则赋值
            else if(matchFunc instanceof Function) {
                let matchRes;
                for(let i1 = 0;i1 < arr1.length;i1++) {
                    matchRes = matchFunc(arr1[i1], arr2[index])
                    if(matchRes) {
                        deepAssign(arr1[i1], arr2[index], matchFunc, notUseMatch);
                        break;
                    }
                }
                if(!matchRes) {
                    arr1.push(arr2[index])
                }
            }

        }else if(type(arr2[index]) === 'Array' && type(arr1[index]) === 'Array') {
            // 多位数组合并情况，仅通过index合并
            deepAssignArr(arr1[index], arr2[index], matchFunc, notUseMatch)
        }else {
            // 非数组对象仅通过赋值合并
            arr1[index] = arr2[index]
        }
    }
}

export function shallowAssignArr(arr1, arr2, matchFunc = null) {
    for (let index in arr2) {
        if(type(arr2[index]) === 'Object') {
            // 匹配规则赋值
            if(matchFunc instanceof Function) {
                let matchRes;
                for(let i1 = 0;i1 < arr1.length;i1++) {
                    matchRes = matchFunc(arr1[i1], arr2[index])
                    if(matchRes) {
                        shallowAssign(arr1[i1], arr2[index], matchFunc);
                        break;
                    }
                }
                if(!matchRes) {
                    arr1.push(arr2[index])
                }
            }
            // 没有自定义匹配规则时使用，顺位赋值
            else {
                if(!isNil(arr1[index])) {
                    shallowAssign(arr1[index], arr2[index], matchFunc);
                }else {
                    arr1.push(arr2[index])
                }
            }
        }else if(type(arr2[index]) === 'Array' && type(arr1[index]) === 'Array') {
            // 多位数组合并情况，仅通过index合并
            shallowAssignArr(arr1[index], arr2[index], matchFunc)
        }else {
            // 非数组对象仅通过赋值合并
            arr1[index] = arr2[index]
        }
    }
}


/**
 * 查询不同值
 * @param json1
 * @param json2
 */
export function deepDiff(json1, json2) {
    let differences = {};

    function compare(obj1, obj2, path) {
        for (let key in obj1) {
            if (!(key in obj2)) {
                differences[path + key] = { left: obj1[key], right: undefined };
            } else {
                if (type(obj1[key]) === 'Object' && type(obj2[key]) === 'Object' || type(obj1[key]) === 'Array' && type(obj2[key]) === 'Array') {
                    let newPath = ''
                    if(path === '') newPath = `#/${key}/`
                    else newPath = path + `${key}/`
                    compare(obj1[key], obj2[key], newPath);
                } else if (obj1[key] !== obj2[key]) {
                    differences[path + key] = { left: obj1[key], right: obj2[key] };
                }
            }
        }

        for (let key in obj2) {
            if (!(key in obj1)) {
                differences[path + key] = { left: undefined, right: obj2[key] };
            }
        }
    }

    compare(json1, json2, '');
    return differences;
}

class Convert {
    reservedProperty: object
    reservedWord: object
    constructor(config) {
        this.reservedProperty = config.reservedProperty || {}
        this.reservedWord = config.reservedWord || {}
    }
    createConverter() {
        return (data) => deepConvert(data, {key: null, parent: null}, this.transValue , this.isTerminator)
    }
    /**
     * 可以把符合条件的字符或者对象转换为目标格式
     * @param data
     * @param params
     * @return {(function(): void)|*}
     */
    transValue(data, params) {
        if(Object.prototype.toString.call(data) === '[object Object]') {
            for(let key in data) {
                if(this.reservedProperty.hasOwnProperty(key)) {
                    return this.reservedProperty[key](data, params)
                }
            }
        }
        if(typeof data === 'string') {
            let regexRes = /^(@[a-zA-Z0-9]+)\/(.+)/.exec(data)
            if(regexRes) {
                const [matchString, $1, $2] = regexRes;
                if(this.reservedWord.hasOwnProperty($1)) {
                    return this.reservedWord[$1](data, params, $2)
                }
            }
        }
        return data
    }
    /**
     * 终止符判断
     * @param data
     * @returns {boolean}
     */
    isTerminator(data) {
        if(Object.prototype.toString.call(data) === '[object Object]') {
            for(let key in data) {
                if(this.reservedProperty.hasOwnProperty(key)) {
                    return true
                }
            }
        }
        if(typeof data === 'string') {
            let regexRes = /^(@[a-zA-Z0-9]+)\/(.+)/.exec(data)
            if(regexRes) {
                const [matchString, $1, $2] = regexRes;
                if(this.reservedWord.hasOwnProperty($1)) {
                    return true
                }
            }
        }
        return false
    }
}

/**
 * 多长度数组组合长度可能性
 * @param sizeArr
 */
export function combine(sizeArr) {
    if(sizeArr.length === 1) return combineSingle(sizeArr[0])
    let res = combineNum(sizeArr[0], sizeArr[1])
    if(sizeArr.length === 2) return res
    for(let i = 2;i < sizeArr.length;i++) {
        res = combineArrSize(res, sizeArr[i])
    }
    return res

    function combineSingle(size) {
        const arr = []
        for(let i = 0;i < size;i++) {
            arr.push([i])
        }
        return arr
    }

    function combineNum(size1, size2) {
        const arr = []
        for(let i = 0;i < size1;i++) {
            for(let j = 0;j < size2;j++) {
                arr.push([i, j])
            }
        }
        return arr
    }

    function combineArrSize(lastArr, size) {
        const arr = []
        for(let item of lastArr) {
            for(let j = 0;j < size;j++) {
                const iArr = clone(item)
                arr.push(iArr.concat(j))
            }
        }
        return arr
    }
}

/**
 * 多组数组组合排列
 * @param groupArr
 */
export function combineArr(groupArr: any[][]) {
    const sizeArr = []
    for(let item of groupArr) {
        sizeArr.push(item.length)
    }
    const indexList = combine(sizeArr)

    const res = []
    for(let indexGroup of indexList) {
        const item = []
        for(let i in indexGroup) {
            let index = indexGroup[i]
            item.push(clone(groupArr[i][index]))
        }
        res.push(item)
    }
    return res
}

// 运行模板字符串
export function runTemplateString(template, variableMap) {
    let keys = [...new Set([...Object.keys(variableMap), ...extractPlaceholders(template)])]
    let values = Object.values(variableMap)
    for(let i = 1;keys.length >= values.length;i++) {
        values.push('')
    }
    let func
    try {
        func = new Function(...keys, `return \`${template}\``)
    }catch (e) {
        console.warn(e)
        return ''
    }
    let res = ''
    try {
        res = func(...values)
    }catch (e) {
        console.warn(e)
        return ''
    }
    return res
}

function extractPlaceholders(str) {
    // 使用正则表达式来匹配 ${} 中的内容
    const regex = /\$\{([a-zA-Z0-9_]+)\}/g;
    const matches = [];
    let match;

    // 使用 while 循环来提取所有匹配项
    while ((match = regex.exec(str)) !== null) {
        // 第一个匹配的分组是 `([a-zA-Z0-9_]+)` 中的内容
        matches.push(match[1]);
    }

    return matches;
}

export default {
    deepConvert, deepMerge, deepAssign, deepDiff, combine, combineArr, shallowAssign, shallowAssignArr
}
